#ifndef __X86_RTL_H__
#define __X86_RTL_H__

#include "rtl/rtl.h"

/* RTL pseudo instructions */

static inline void rtl_lr(rtlreg_t* dest, int r, int width) {
  switch (width) {
    case 4: rtl_mv(dest, &reg_l(r)); return;
    case 1: rtl_host_lm(dest, &reg_b(r), 1); return;
    case 2: rtl_host_lm(dest, &reg_w(r), 2); return;
    default: assert(0);
  }
}

static inline void rtl_sr(int r, const rtlreg_t* src1, int width) {
  switch (width) {
    case 4: rtl_mv(&reg_l(r), src1); return;
    case 1: rtl_host_sm(&reg_b(r), src1, 1); return;
    case 2: rtl_host_sm(&reg_w(r), src1, 2); return;
    default: assert(0);
  }
}

static inline void rtl_push(const rtlreg_t* src1) {
  // esp <- esp - 4
  // M[esp] <- src1
  //TODO();

  cpu.esp = cpu.esp-4;
  rtl_sm(&cpu.esp, src1, 4);
}

static inline void rtl_pop(rtlreg_t* dest) {
  // dest <- M[esp]
  // esp <- esp + 4
  //TODO()

  rtl_lm(dest, &cpu.esp, 4);
  cpu.esp = cpu.esp+4;

}

static inline void rtl_is_sub_overflow(rtlreg_t* dest,
    const rtlreg_t* res, const rtlreg_t* src1, const rtlreg_t* src2, int width) {
  // dest <- is_overflow(src1 - src2)
  //TODO();
  rtl_xor(&t0, src1, src2);
  rtl_xor(&t1, res, src1);
  rtl_and(&t0, &t0, &t1);
  rtl_msb(dest, &t0, width);
}

static inline void rtl_is_sub_carry(rtlreg_t* dest,
    const rtlreg_t* res, const rtlreg_t* src1) {
  // dest <- is_carry(src1 - src2)
  //TODO();
  rtl_setrelop(RELOP_LTU, dest, src1, res);
}

static inline void rtl_is_add_overflow(rtlreg_t* dest,
    const rtlreg_t* res, const rtlreg_t* src1, const rtlreg_t* src2, int width) {
  // dest <- is_overflow(src1 + src2)
  //TODO();
  rtl_not(&t0, src1);
  rtl_xor(&t1, &t0, src2);
  rtl_xor(&t0, res, src1);
  rtl_and(&t0, &t0, &t1);
  rtl_msb(dest, &t0, width);
}

static inline void rtl_is_add_carry(rtlreg_t* dest,
    const rtlreg_t* res, const rtlreg_t* src1) {
  // dest <- is_carry(src1 + src2)
  //TODO();
  // printf("entering rtl is add carry----------------------------------------------------\n");
  // printf("res is %x, src1 is %x\n", *res, *src1);
  rtl_setrelop(RELOP_LTU, dest, res, src1);
  // printf("CF set to %x\n", *dest);
}

#define make_rtl_setget_eflags(f) \
  static inline void concat(rtl_set_, f) (const rtlreg_t* src) { \
    if (*src==0) cpu.eflags.f = 0; else cpu.eflags.f = 1;\
  } \
  static inline void concat(rtl_get_, f) (rtlreg_t* dest) { \
    *dest = cpu.eflags.f; \
  }

make_rtl_setget_eflags(CF)
make_rtl_setget_eflags(OF)
make_rtl_setget_eflags(ZF)
make_rtl_setget_eflags(SF)

static inline void rtl_update_ZF(const rtlreg_t* result, int width) {
  // eflags.ZF <- is_zero(result[width * 8 - 1 .. 0])
  //TODO();
  rtl_shli(&t0, result, 32 - width * 8);
  if(t0 == 0) cpu.eflags.ZF = 1;
  else cpu.eflags.ZF = 0;
  // printf("ZF_updated!  result is %x, flag is %u\n", *result, cpu.eflags.ZF);
}

static inline void rtl_update_SF(const rtlreg_t* result, int width) {
  // eflags.SF <- is_sign(result[width * 8 - 1 .. 0])
  //TODO();
  cpu.eflags.SF = (*result>>(width*8-1))&0b1;
  // printf("SF_updated!  result is %x, flag is %u\n", *result, cpu.eflags.SF);
}

static inline void rtl_update_ZFSF(const rtlreg_t* result, int width) {
  rtl_update_ZF(result, width);
  rtl_update_SF(result, width);
}

#endif
